﻿using ATM.Model;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;

namespace ATM.Components
{

    /// <summary>
    /// Provides access to cash dispenser database.
    /// </summary>
    class CashDispenserDb
    {
        string dbPath = @"Resources\CashDispenserDb.xml";
        string schemaPath = @"Resources\CashDispenserDb.xsd";
        public Trays trays { get; private set; }

        XmlSerializer xmlSerializer;

        public CashDispenserDb()
        {
            XmlReaderSettings readerSettings = new XmlReaderSettings();
            readerSettings.Schemas.Add("atm", schemaPath);

            xmlSerializer = new XmlSerializer(typeof(Trays));
            using (XmlReader reader = XmlReader.Create(dbPath, readerSettings))
            {
                trays = (Trays)xmlSerializer.Deserialize(reader);
            }
        }

        internal Tray[] AvailableBills()
        {
            Contract.Requires(trays != null);
            Contract.Requires(trays.Items != null);
            Contract.Requires(trays.Items.Length > 0);

            var sorted = (from ts in trays.Items where ts.BillCount > 0 select ts).OrderByDescending(i => i.BillDenomination).ThenBy(i => i.BillCount);
            return sorted.ToArray<Tray>();
        }

        internal bool SubtractBill(int denomination)
        {
            Contract.Requires(trays != null);
            Contract.Requires(trays.Items != null);
            Contract.Requires(trays.Items.Length > 0);

            return SubtractBill(denomination, 1);
        }

        internal bool SubtractBill(int denomination, int amount)
        {
            Contract.Requires(trays != null);
            Contract.Requires(trays.Items != null);
            Contract.Requires(trays.Items.Length > 0);
            Contract.Requires(amount > 0);
            Contract.Exists(trays.Items, t => t.BillDenomination == denomination);

            var tray = (from ts in trays.Items where (ts.BillDenomination == denomination && ts.BillCount >= amount) select ts).First<Tray>();
            if (tray == null)
            {
                return false;
            }

            tray.BillCount = tray.BillCount - amount;

            synchronize();
            return true;
        }

        void synchronize()
        {
            Contract.Requires(xmlSerializer != null);
            Contract.Requires(!string.IsNullOrEmpty(this.dbPath));

            using (XmlWriter writer = XmlWriter.Create(dbPath))
            {
                xmlSerializer.Serialize(writer, trays);
            }
        }
    }
}
